/*
 * Copyright 2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.seppiko.chart.services;

import com.d_project.qrcode.QRCode;
import com.google.zxing.WriterException;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import org.seppiko.chart.exceptions.SeppikoCheckException;
import org.seppiko.chart.exceptions.SeppikoProcessorException;
import org.seppiko.chart.models.BarcodeEntity;
import org.seppiko.chart.models.ControllerEntity;
import org.seppiko.chart.models.MessageEntity;
import org.seppiko.chart.utils.DProjectUtil;
import org.seppiko.chart.utils.ImageUtil;
import org.seppiko.chart.utils.JsonUtil;
import org.seppiko.chart.utils.ZxingUtil;
import org.seppiko.chart.utils.logging.LoggingManager;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

/**
 * @author Leonard Woo
 */
@Service
public class QrCodeService {

  private static final LoggingManager Logger = LoggingManager.getInstance().getLogger(QrCodeService.class.getName());

  public ControllerEntity qrCodeCHX(BarcodeEntity entity) {
    ControllerEntity resp = new ControllerEntity();
    ImageUtil imageUtil = new ImageUtil();
    try {
      resp.setStatus(200);
      ByteMatrix matrix = ZxingUtil.getQrCode().encode(entity);
      if (entity.getFormat().toLowerCase().matches("t[e]?xt")) {
        resp.setMediaType(MediaType.TEXT_PLAIN);
        resp.setData( ZxingUtil.convertByteArrays( matrix.getArray() ).getBytes() );
      } else if (entity.getFormat().equalsIgnoreCase("svg")) {
        resp.setMediaType(MediaType.parseMediaType("image/svg+xml"));
        resp.setData(imageUtil.svgGenerator(entity, matrix));
      } else {
        resp.setData(imageUtil.genImage(entity, matrix));
        resp.setMediaType(imageUtil.getType());
      }
    } catch (WriterException ex) {
      String outMsg = "QR Code Generator Fail!!!";
      Logger.error(outMsg, ex);
      resp.setStatus(500);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setData( JsonUtil.toJson( new MessageEntity(500, outMsg) ).getBytes() );
    } catch (SeppikoCheckException ex) {
      Logger.error(ex.getMessage(), ex);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setStatus(501);
      resp.setData(JsonUtil.toJson(new MessageEntity(501, ex.getMessage())).getBytes());
    } catch (SeppikoProcessorException ex) {
      Logger.error("Create failed", ex);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setStatus(500);
      resp.setData(JsonUtil.toJson(new MessageEntity(500, "Create failed")).getBytes());
    }
    return resp;
  }

  public ControllerEntity qrCodeDCHX(BarcodeEntity entity, boolean customFlag) {
    ControllerEntity resp = new ControllerEntity();
    resp.setStatus(200);
    ImageUtil imageUtil = new ImageUtil();
    entity.setNeedText(false); //Close bottom text
    try {
      QRCode qrCode = DProjectUtil.getQRCode(entity.getData(), entity.getType(),
          DProjectUtil.parseErrorCorrectionLevel( entity.getErrorCorrectionLevel() ) );

      ByteMatrix matrix = DProjectUtil.convertToZxingByteMatrix(qrCode);
      entity.setWidth(matrix.getWidth());
      entity.setHeight(matrix.getHeight());
      if (entity.getFormat().toLowerCase().matches("t[e]?xt")) {
        resp.setMediaType(MediaType.TEXT_PLAIN);
        resp.setData( ZxingUtil.convertByteArrays( matrix.getArray() ).getBytes() );
      } else if (entity.getFormat().equalsIgnoreCase("svg")) {
        resp.setMediaType(MediaType.parseMediaType("image/svg+xml"));
        resp.setData(imageUtil.svgGenerator(entity, matrix));
      } else {
        if (customFlag &&
            !(entity.getNeedBase64() || entity.getEnableJson() || entity.getNeedText()) ) {
          resp.setData(imageUtil.genDImg(entity, qrCode));
        } else {
          resp.setData(imageUtil.genImage(entity, matrix));
        }
        resp.setMediaType(imageUtil.getType());
      }
    } catch (SeppikoCheckException ex) {
      Logger.error(ex.getMessage(), ex);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setStatus(501);
      resp.setData(JsonUtil.toJson(new MessageEntity(501, ex.getMessage())).getBytes());
    } catch (SeppikoProcessorException ex) {
      Logger.error("Create failed", ex);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setStatus(500);
      resp.setData(JsonUtil.toJson(new MessageEntity(500, "Create failed")).getBytes());
    }
    return resp;
  }

  public ControllerEntity qrCodeXCHX(BarcodeEntity entity) {
    ControllerEntity resp = new ControllerEntity();
    resp.setStatus(200);
    ImageUtil imageUtil = new ImageUtil();
    try {
      QRCode qrCode = DProjectUtil.getQRCode(entity.getData(), 0,
          DProjectUtil.parseErrorCorrectionLevel( entity.getErrorCorrectionLevel() ) );

      ByteMatrix matrix = DProjectUtil.convertToZxingByteMatrix( qrCode );
      if (entity.getFormat().toLowerCase().matches("t[e]?xt")) {
        resp.setMediaType(MediaType.TEXT_PLAIN);
        resp.setData( ZxingUtil.convertByteArrays( matrix.getArray() ).getBytes() );
      } else if (entity.getFormat().equalsIgnoreCase("svg")) {
        resp.setMediaType(MediaType.parseMediaType("image/svg+xml"));
        resp.setData(imageUtil.svgGenerator(entity, matrix));
      } else {
        resp.setData(imageUtil.genImage(entity, matrix));
        resp.setMediaType(imageUtil.getType());
      }
    } catch (SeppikoCheckException ex) {
      Logger.error(ex.getMessage(), ex);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setStatus(501);
      resp.setData(JsonUtil.toJson(new MessageEntity(501, ex.getMessage())).getBytes());
    } catch (SeppikoProcessorException ex) {
      Logger.error("Create failed", ex);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setStatus(500);
      resp.setData(JsonUtil.toJson(new MessageEntity(500, "Create failed")).getBytes());
    }
    return resp;
  }
}
